package com.bytenebula.asteroid.service.impl;

import com.bytenebula.asteroid.ByteNebulaException;
import com.bytenebula.asteroid.DynamicDataSource;
import com.bytenebula.asteroid.ResultCodes;
import com.bytenebula.asteroid.entity.MetaColumn;
import com.bytenebula.asteroid.entity.MetaTable;
import com.bytenebula.asteroid.service.ExtractService;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public abstract class ExtractServiceImpl implements ExtractService {

    /**
     * 根据数据库类型获取数据库抽取器实例
     *
     * @param dbType 数据库类型
     * @return 返回数据库抽取器对象实例，如果无法找到对应的数据库类型返回NULL
     */
    public static com.bytenebula.asteroid.service.ExtractService createExtractService(String dbType) {
        String uppercase_db_type = dbType.toUpperCase();

        return switch (uppercase_db_type) {
            case "MYSQL" -> new MysqlExtractService();
            case "MSSQL" -> new MssqlExtractService();
            default -> throw new ByteNebulaException(ResultCodes.INVALID_DB_TYPE, dbType);
        };
    }

    /**
     * 读取数据库中的所有表
     *
     * @param db_name 待抽取数据库名称
     * @param sql 查询语句
     * @return 返回数据表列表
     */
    protected List<MetaTable> fetchTables(String db_name, String sql) {
        List<MetaTable> tables = new ArrayList<>();
        try (Connection connection = DynamicDataSource.getConnection(db_name)) {
            if (connection != null) {
                PreparedStatement stmt = connection.prepareStatement(sql);
                ResultSet results = stmt.executeQuery();
                if (results != null) {
                    while (results.next()) {
                        MetaTable table = buildTable(results);
                        table.setId(tables.size() + 1);
                        tables.add(table);
                    }
                }
            }
            return tables;
        } catch (SQLException | MalformedURLException | ClassNotFoundException | InvocationTargetException |
                 NoSuchMethodException | InstantiationException | IllegalAccessException e) {
            System.out.println(e.getLocalizedMessage());
            return null;
        }
    }

    /**
     * 构建MetaTable对象
     * @param results 数据查询结果集
     * @return 返回MetaTable对象
     */
    protected MetaTable buildTable(ResultSet results) throws SQLException {
        MetaTable table = new MetaTable();
        table.setTableName(results.getString("TABLE_NAME"));
        table.setTableSchema(results.getString(("TABLE_SCHEMA")));
        table.setTableType(results.getString("TABLE_TYPE"));
        return table;
    }

    /**
     * 读取数据表中的所有字段
     * @param db_name 待抽取数据库名称
     * @param sql 查询语句
     * @return 返回数据字段列表
     */
    protected List<MetaColumn> fetchColumns(String db_name, String sql) {
        List<MetaColumn> columns = new ArrayList<>();
        try (Connection connection = DynamicDataSource.getConnection(db_name)) {
            if (connection != null) {
                PreparedStatement stmt = connection.prepareStatement(sql);
                ResultSet results = stmt.executeQuery();
                if (results != null) {
                    while (results.next()) {
                        MetaColumn col = buildColumn(results);
                        col.setId(columns.size()+1);
                        columns.add(col);
                    }
                }
            }
            return columns;
        } catch (SQLException | MalformedURLException | ClassNotFoundException | InvocationTargetException |
                 NoSuchMethodException | InstantiationException | IllegalAccessException e) {
            System.out.println(e.getLocalizedMessage());
            return null;
        }
    }

    /**
     * 构建MetaColumn对象
     * @param results 数据查询结果集
     * @return 构建MetaColumn对象
     */
    protected MetaColumn buildColumn(ResultSet results) throws SQLException {
        MetaColumn col = new MetaColumn();
        col.setIsPk(0);
        col.setColumnName(results.getString("COLUMN_NAME"));
        col.setColumnIndex(results.getInt("ORDINAL_POSITION"));
        col.setDefaultValue(results.getString("COLUMN_DEFAULT"));
        col.setIsNullable(Objects.equals(results.getString("IS_NULLABLE"), "YES") ? 1 : 0);
        col.setColumnType(results.getString("DATA_TYPE"));
        int precision = results.getInt("NUMERIC_PRECISION");
        if (precision > 0) {
            col.setColumnPrecious(precision);
            col.setColumnScale(results.getInt("NUMERIC_SCALE"));
        } else {
            col.setColumnPrecious(results.getInt("CHARACTER_MAXIMUM_LENGTH"));
        }
        col.setComment(results.getString("COLUMN_COMMENT"));
        String columnKey = results.getString("COLUMN_KEY");
        col.setIsPk(Objects.equals(columnKey, "PRI") ? 1 : 0);
        return col;
    }
}
